﻿using System;
using System.Threading;

using Microsoft.SPOT.Hardware;

using SecretLabs.NETMF.Hardware.Netduino;

using OST.Shared.Interfaces;
using OST.Shared.Extensions;
using mfLogger;

namespace OST.Anemometer
{
    /// <summary>
    /// class 4 gathering the wind speed based on a reed-sensor-anemometer, implements <see cref="IAnemometer"/>
    /// </summary>
    public class ReedAnemometer : IAnemometer
    {
        #region Consts&Fields

        Cpu.Pin C_INTERRUPT_PORT = Pins.GPIO_PIN_D13;
        /// <summary>
        /// calculation wait-cycle
        /// </summary>
        const int C_INT_CALC_UPDATE_CYCLE = 2700;
        /// <summary>
        /// windspeed-average, number of windspeed-values to be averaged
        /// </summary>
        const int C_INT_AVE_WINDSPEED_COUNT = 200;//~10 minute-average @2700 ms 
        /// <summary>
        /// Interrupts per 'reed-contact'
        /// </summary>
        const int C_INT_NUM_REED_ERROR = 9;
        /// <summary>
        /// transformation from rounds-per-second 2 windspeed in ms
        /// </summary>
        const double C_DBL_MEASURE_VALUE = 2.3;

        object _sync = new object();

        InterruptPort _portIn = null;
        Timer _timer = null;

        int _reedCounter = 0;
        double _windSpeed = 0.0;

        int _numOfWindSpeeds = 0;
        int _lastWindSpeedIndex = 0;
        double[] _arrLastWindSpeeds = new double[C_INT_AVE_WINDSPEED_COUNT];

        #endregion

        #region CTOR

        public ReedAnemometer()
        {
            _portIn = new InterruptPort(C_INTERRUPT_PORT, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeHigh);
        }

        #endregion


        #region Methods

        private void _portIn_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            //increment counter
            _reedCounter++;
        }

        private void _TimerTick(object state)
        {
            if (_reedCounter > 0)
            {
                lock (_sync)
                {
                    _portIn.OnInterrupt -= _portIn_OnInterrupt;
                    //calc rounds/ second
                    double RpS = (_reedCounter / C_INT_NUM_REED_ERROR);
                    //calc timer offset
                    RpS = RpS / (C_INT_CALC_UPDATE_CYCLE / 1000.0);
                    //calc wind speed in ms
                    _windSpeed = RpS * C_DBL_MEASURE_VALUE;
                    //reset reed-counter
                    _reedCounter = 0;
                    _portIn.OnInterrupt += _portIn_OnInterrupt;
                }
            }
            else if (_reedCounter == 0 && _windSpeed > 0.0)
            {
                _reedCounter = 0;
                _windSpeed = 0.0;
            }

            //calculate new history-array-index
            _lastWindSpeedIndex = (_lastWindSpeedIndex + 1) % _arrLastWindSpeeds.Length;
            //save the windspeed in history-array
            _arrLastWindSpeeds[_lastWindSpeedIndex] = _windSpeed;
            //calculate the numOfWindSpeed 4 average calculation
            if (_lastWindSpeedIndex > (_arrLastWindSpeeds.Length - 1))
                _numOfWindSpeeds = _arrLastWindSpeeds.Length - 1;
            else 
                _numOfWindSpeeds = _lastWindSpeedIndex;

        }

        #endregion

        #region Properties

        /// <summary>
        /// wrapper-property 4 logging
        /// </summary>
        private ILoggerAppender Logger
        {
            get
            {
                return mfLogger.mfLogger.Instance;
            }
        }

        #endregion

        #region IAnemometer

        public void Start()
        {
            _reedCounter = 0;
            _windSpeed = 0;

            _numOfWindSpeeds = 0;
            _lastWindSpeedIndex = 0;
            _arrLastWindSpeeds = new double[C_INT_AVE_WINDSPEED_COUNT];

            _timer = new Timer(new TimerCallback(_TimerTick), null, C_INT_CALC_UPDATE_CYCLE, C_INT_CALC_UPDATE_CYCLE);
            _portIn.OnInterrupt += _portIn_OnInterrupt;

            Logger.Debug(GetType() + " Start executed");
        }

        public void Stop()
        {
            _reedCounter = 0;
            _windSpeed = 0.0;

            _numOfWindSpeeds = 0;
            _lastWindSpeedIndex = 0;
            _arrLastWindSpeeds = new double[C_INT_AVE_WINDSPEED_COUNT];

            _timer.Dispose();
            _timer = null;

            _portIn.OnInterrupt -= _portIn_OnInterrupt;

            Logger.Debug(GetType() + " Stop executed");
        }

        public double WindSpeed
        {
            get { return _windSpeed; }
        }

        public double WindSpeedAverage
        {
            get 
            {
                var sumOfValues = 0.0;
                var numOfValues = (_numOfWindSpeeds == 0.0)?_arrLastWindSpeeds.Length:_lastWindSpeedIndex;
                for (int i = 0; i < numOfValues; i++)
                {
                    sumOfValues += _arrLastWindSpeeds[i];   
                }
                return sumOfValues / numOfValues;
            }
         }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// notifies clients over changes in puplic properties
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnNotifyPropertyChanged(String propertyName, object newValue)
        {

            if (PropertyChanged != null && !propertyName.IsNullOrEmpty())
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName, newValue));
            }
        }

        #endregion
    }
}
